developmentor - a developer services company				  
				      Essential .NET Security
   Modules

CAS Enforcement

Estimated time for completion: 60 minutes


Goals of this exercise:

  • Experiment with an Intranet deployed Windows Forms application
  • Learn firsthand what happens when code developed locally goes mobile
  • Deploy dependent assemblies so mobile code can call them
  • Experiment with custom code access permissions

Overview

In this activity you'll experiment with deploying a Windows Forms application via the Intranet, and fix a problem that arises due to the code being mobile and thus partially trusted. You'll then fix another problem that occurs when you deploy multiple assemblies this way. Finally, you'll spend some time experimenting with an implementation of a custom code access permission.


part 1- Writing mobile code for the local Intranet

An application has been provided for you, AcmeExpense, which is a simple front end for a corporate expense tracking application. The idea is that if you need to record an expense, you click a link on the corporate Intranet and this Windows Forms client pops up and takes your information.

The problem is, much of the information is repetitive. A programmer on your team has added a persistence feature recently, to persist the most commonly reused fields on the client's hard drive. It works great on his machine, but he's come to you (the .NET expert) complaining that it doesn't work when deployed over the corporate Intranet.

Your job is to fix the problem, not by eliminating the functionality he added, but by choosing a different storage vehicle that is allowed by default in the LocalIntranet zone.

steps:

  1. Load the AcmeExpense project and build it. It should build without any errors.

  2. Map a virtual directory (call it AcmeExpense, please) to the following path in your working directory: 07_CASEnforcement\ZeroDeployment\before\AcmeExpense\bin\debug. If you need help creating a vdir, the instructor can help you, please ask)

  3. Now try to reproduce the problem. Run AcmeExpense by clicking here.

  4. What happens? You should be seeing a SecurityException on startup as the app goes poking around your hard drive, looking for its settings file.

  5. Time to fix the problem. Bring up the AcmeExpense project and view the code behind the main form (from the solution explorer, right-click MainForm.cs and choose "View Code").

  6. Fix the problem by using isolated storage. Hint: the only code you need to muck with is _getStorageForUserSettings(), which can be found at the bottom of MainForm.cs.

    Note that the callers of _getStorageForUserSettings() assume that it returns null if they ask for a read-only stream and the settings file doesn't exist yet. You can use IsolatedStorageFile.GetFileNames() to test for existence of a logical file in the isolated store.

    You're on your own here - ask the instructor if you need further hints, or if you panic, have a look at the solution (links to solutions are at the bottom of each lab).


part 2- Deploying multiple assemblies

In this part, you'll have AcmeExpense attempt to use the services of a second assembly, which will cause a SecurityException to be thrown. Your job is to figure out why the exception is occurring, and fix the problem.

steps:

  1. In MainForm.cs, browse to the okButton_Click() event handler and notice that there's a line of code that's currently commented out. Have a look at the Helper.cs file to see what this commented out function does, then uncomment it, build, then click here to run the app. What happens?

  2. You should be seeing a security exception. Your job is to figure out what the heck is going on and remedy the problem. If you spend more than 10 minutes and haven't figured out the problem, ask the instructor for a hint, or if all else fails, look at the solution (MainForm.cs) below.

    NOTE: if you make any changes to the Helper assembly, you need to increment the version number and rebuild both it and the AcmeExpense project, otherwise your client will not see the change - she will continue to use the version in her local download cache.


part 3- Custom Permissions

If you really want to understand how CAS enforcement works, the best way to do it is to write your own custom permission class. It's not as much work as you might think, and we've provided an example here to get you started.

Besides exploration of the technology, custom permissions will probably be used in the real world to gate access to custom functionality that's provided by native code. In other words, instead of having to grant code full trust so it can use interop to directly invoke your native code, you can provide gateway classes (similar to FileStream, HttpWebRequest, and SqlConnection, which act as gateways to OS and database resources). You can then use policy to state which code is allowed to use those gateways. This finer grained control of security policy allows you to avoid granting too many permissions to code you don't otherwise trust.

steps:

  1. Open up the BeepPermission solution, and note that it contains three projects:

    1. MyPermissions - the custom permission itself
    2. Beeper - the gateway class that demands the permission
    3. TestApp - a test application that uses the Beeper gateway

  2. Peruse the BeepPermission.cs source file (in the MyPermissions project). Take some time and look at the code, reading the comments throughout. There are two classes here: the permission and an attribute class that allows the permission to be used declaratively.

  3. Next take a look at the Beeper class, which is a very simple gateway to the computer speaker that demands the permission you just looked at. The idea is that partially trusted code that wouldn't normally be able to beep the speaker can use this gateway if they have been granted the beep permission by policy.

    Note that the whole point to this exercise is to be able to grant fine grained permissions to code - we can now allow code to beep the speaker without having to make it fully trusted. This is the whole point of code access security. Beeping the speaker isn't very interesting, but it's a simple enough example to get the point across and show how the mechanisms work.

  4. Finally, take a look at TestApp.cs (in the TestApp project). This app makes use of the Beeper class as a gateway to beep the speaker.

  5. Build the solution. It should build without any troubles, and the implementation of each part is complete. Your job is to integrate the beep permission into policy and give it a try.

  6. Whenever you create a custom code access permission, you need to register the assembly that contains the permission in the GAC. Then you need to go one step further, and register it as a "policy assembly" so that it'll always be granted full trust no matter what. If you don't do this, you're not guaranteed that the policy engine will be able to use your permission in all cases. Because of this elevated level of trust, you should do as we've done here, and factor out your permissions into a separate assembly, so you only elevate trust for the code that really needs it.

    steps:

    1. First, register MyPermission.dll in the GAC (here's the directory where it lives).

    2. Now bring up mscorcfg.msc in an administrative context and drill down into the Runtime Security Policy/Machine/Policy Assemblies node. Right click this folder and choose "Add...". This will bring up a dialog listing assemblies in the GAC - find MyPermissions.dll and add it. Please note that each policy level (Enterprise, Machine, User) has a similar folder, so if you wanted to include your custom permission in, say, the Enterprise policy, you'd need to add it to the Enterprise policy level's list of trusted policy assemblies. We're just going to experiment with machine policy for this lab.

  7. The Beeper class is a gateway that demands the BeepPermission then beeps the computer speaker by calling out to native code. This gateway must be fully trusted to make this native call (just like FileStream and friends are trusted). In mscorcfg.msc, add a code group in the Machine policy to reflect this. The safest way to do this is to add it as a child of the My_Computer_Zone code group. The membership condition should be of type "Strong Name", and you can use the Import button to get all the information about the strong name from the Beeper.dll assembly. Be sure to check the "Name" and "Version" checkbox as well.

  8. Now that our permission is in a trusted policy assembly, we can start using it in the Machine policy level. We'll programmatically create a permission set and add it to policy in this step.

    steps:

    1. Bring up TestApp.cs and uncomment the call to WritePermissionSet. Run TestApp.exe and redirect the results to an XML file in your root directory, or somewhere easy to find.

    2. Go back to mscorcfg.msc (as admin) and drill into the machine security policy. Right click the Permission Sets node and say you want to add a new permission set. On the first page of the resulting dialog, select the radio button that imports from a file. Import the XML file you just created programmatically - this grants our BeepPermission, and nothing else.

  9. Drill into the Code Groups node for the machine security policy, and find the My_Computer_Zone code group. Bring up its property sheet and change the permission set it points to from FullTrust to LocalIntranet (a.k.a. medium trust). This should keep you from being able to beep the speaker by calling directly to Win32.

    In TestApp.cs, comment out the call to WritePermissionSet and uncomment the call to CallMessageBeepDirectly. Then build and run to verify that you're no longer allowed to make random calls to native code.

  10. Comment out the call to CallMessageBeepDirectly, and uncomment CallBeeper. You're now using a gateway class to attempt something you wouldn't have been able to do directly. Give it a try - does it succeed or fail?.

    Even using the gateway should fail at the moment, because you've not granted local code the BeepPermission - you don't yet have the right to get through that particular gate.

  11. Time to open up the gate. Go back to mscorcfg.msc (as an admin) and add a code group as a child of the My_Computer_Zone code group. Call it "Beep" or whatever you like. Set the membership condition to "All Code" so it matches all code that's on your local machine (remember this child code group won't match unless its parent matches as well). Point it at BeepPermission, the named permission set you imported earlier.

    Try again to use the gate - it should work now. You should also verify that you still are not allowed to call MessageBeep directly.

  12. At this point it'd be useful to have another look at the Beeper class to see how it does its work. Why can you call through the Beeper class and do something that you wouldn't be allowed to do directly? What would happen if you tried to use the same technique and apply the SuppressUnmanagedCodeSecurity attribute to the Native class in TestApp.cs? Would that allow you make the call directly? Try it if you're not sure.

  13. Go back to TestApp.cs and uncomment the two assembly level attributes (close to the top of the source file). Try to figure out what these mean, and how their presence will impact TestApp. Run it using CallBeeper(3) to check your conclusions. Ask the instructor if you're not entirely sure what these attributes do (this is a bit of a review from the lecture on Policy).

Solutions: